using System.Collections.Generic;
using UnityEngine;
using VoxelEngine;
using VoxelEngine.Utils;

namespace VoxelGame.NPC
{
	[RequireComponent(typeof(CharacterController))]
	public class NPCControler : MonoBehaviour, INPC
	{
		[SerializeField]
		private NPCBehaviour[] _npcBehaviours;

		[SerializeField]
		private Animator _animator;

		[SerializeField]
		private SkinnedMeshRenderer _renderer;

		[SerializeField]
		private NPCBehaviour _defaultBehaviour;

		[SerializeField]
		private NPCBehaviour _interactBehaviour;

		[SerializeField]
		private CharacterController _characterController;

		[SerializeField]
		private BlobShadow _blobShadow;

		[SerializeField]
		private Vector3 _initialPosition;

		[SerializeField]
		private string _fallAnimation;

		[SerializeField]
		private AudioSource _audioSource;

		private VoxelEngine.World _world;

		private Vector3i _position = default(Vector3i);

		private Vector3i _prevPosition = default(Vector3i);

		private float _currentLight;

		private float _targetLight;

		private float _fallTime;

		private bool _wasGrounded;

		private NPCBehaviour _currentBehaviour;

		private string _currentAnimation;

		private Block _standingOverBlock;

		protected readonly List<INPCListener> _npcListeners = new List<INPCListener>();

		private void Awake()
		{
			base.gameObject.RemoveCloneFromName();
			NPCBehaviour[] npcBehaviours = _npcBehaviours;
			foreach (NPCBehaviour nPCBehaviour in npcBehaviours)
			{
				nPCBehaviour.enabled = false;
				nPCBehaviour.Init(this);
			}
			if (_audioSource != null)
			{
				_audioSource.spatialBlend = 1f;
				_audioSource.minDistance = 5f;
				_audioSource.maxDistance = 20f;
				_audioSource.rolloffMode = AudioRolloffMode.Linear;
			}
			_characterController.enableOverlapRecovery = true;
		}

		private void Start()
		{
			base.transform.position = _initialPosition;
		}

		public void Init(VoxelEngine.World world)
		{
			_world = world;
			EnableBehaviour(_defaultBehaviour);
		}

		public void OnBehaviourFinished(NPCBehaviour npcBehaviour = null)
		{
			if (npcBehaviour == _interactBehaviour)
			{
				EnableBehaviour(_defaultBehaviour);
			}
			else
			{
				ActiveRandomBehaviour(npcBehaviour);
			}
		}

		public bool SimpleMove(Vector3 speed)
		{
			return _characterController.SimpleMove(speed);
		}

		public float ColliderRadius()
		{
			return _characterController.radius;
		}

		public void PlaySound(AudioClip audioClip)
		{
			if (_audioSource != null)
			{
				_audioSource.clip = audioClip;
				_audioSource.Play();
			}
			else
			{
				UnityEngine.Debug.LogWarning("[" + base.gameObject.name + "] This object is trying to play an audio without a AudioSource!");
			}
		}

		public void PlayAnimation(string animation = "")
		{
			if (string.IsNullOrEmpty(animation))
			{
				animation = _currentAnimation;
			}
			_animator.SetTrigger(animation);
			_currentAnimation = animation;
		}

		public string GetCurrentAnimation()
		{
			return _currentAnimation;
		}

		public Vector3 GetInitialPosition()
		{
			return _initialPosition;
		}

		private void EnableBehaviour(NPCBehaviour npcBehaviour)
		{
			if (_currentBehaviour != null)
			{
				_currentBehaviour.enabled = false;
			}
			_currentBehaviour = npcBehaviour;
			_currentBehaviour.enabled = true;
		}

		private void ActiveRandomBehaviour(NPCBehaviour npcBehaviour = null)
		{
			if (npcBehaviour != null)
			{
				npcBehaviour.enabled = false;
			}
			NPCBehaviour nPCBehaviour = _npcBehaviours[Random.Range(0, _npcBehaviours.Length)];
			if (nPCBehaviour == _interactBehaviour)
			{
				nPCBehaviour = _defaultBehaviour;
			}
			EnableBehaviour(nPCBehaviour);
		}

		private void Update()
		{
			if (_renderer.isVisible)
			{
				UpdateStandingOverBlock();
				UpdateNPCLight();
				UpdateCharacterController();
				UpdateGravity();
				UpdateInteraction();
			}
			DestroyIfNeeded();
		}

		private void UpdateInteraction()
		{
			if (_interactBehaviour != null && Input.GetMouseButtonDown(0))
			{
				Ray ray = Camera.main.ScreenPointToRay(UnityEngine.Input.mousePosition);
				if (Physics.Raycast(ray, out RaycastHit hitInfo, 10f) && hitInfo.transform == base.transform)
				{
					EnableBehaviour(_interactBehaviour);
					NotifyNpcInteraction(base.name);
				}
			}
		}

		private void DestroyIfNeeded()
		{
			Vector3 position = base.transform.position;
			if (position.y < -1f)
			{
				UnityEngine.Object.Destroy(base.gameObject);
			}
		}

		private void UpdateStandingOverBlock()
		{
			ref Vector3i position = ref _position;
			Vector3 position2 = base.transform.position;
			position.x = (int)position2.x;
			ref Vector3i position3 = ref _position;
			Vector3 position4 = base.transform.position;
			position3.y = (int)position4.y;
			ref Vector3i position5 = ref _position;
			Vector3 position6 = base.transform.position;
			position5.z = (int)position6.z;
			if (_position != _prevPosition)
			{
				_standingOverBlock = _world[_position.x, _position.y, _position.z];
			}
			_prevPosition = _position;
		}

		private void UpdateNPCLight()
		{
			if (_standingOverBlock != null)
			{
				_targetLight = (float)(int)_standingOverBlock.Light / 255f;
			}
			_currentLight = Mathf.Lerp(_currentLight, _targetLight, Time.deltaTime * 4f);
			_renderer.material.SetFloat("_AmbientLight", _currentLight);
			_blobShadow.SetAmbientLight(_currentLight);
		}

		private void UpdateCharacterController()
		{
			if (_wasGrounded != _characterController.isGrounded)
			{
				IsGroundStateChanged(_characterController.isGrounded);
			}
		}

		private void UpdateGravity()
		{
			if (!_characterController.isGrounded)
			{
				_fallTime += Time.deltaTime;
				if (_fallTime > 0.5f && _currentAnimation != _fallAnimation)
				{
					_fallTime = 0f;
					PlayAnimation(_fallAnimation);
				}
			}
		}

		private void IsGroundStateChanged(bool grounded)
		{
			_wasGrounded = _characterController.isGrounded;
			_fallTime = 0f;
			if (grounded)
			{
				_currentBehaviour.PlayCurrentAnimation();
			}
		}

		public void RegisterNpcListener(INPCListener npcListener)
		{
			_npcListeners.Add(npcListener);
		}

		private void NotifyNpcInteraction(string npc)
		{
			foreach (INPCListener npcListener in _npcListeners)
			{
				npcListener.OnNpcInteraction(npc);
			}
		}
	}
}
